package definition

import (
	"encoding/json"
	"fmt"
	"time"
)

// WorkflowType represents the workflow type
type WorkflowType string

const (
	// WorkflowTypeSequential represents a sequential workflow
	WorkflowTypeSequential WorkflowType = "sequential"
	// WorkflowTypeParallel represents a parallel workflow
	WorkflowTypeParallel WorkflowType = "parallel"
	// WorkflowTypeDAG represents a directed acyclic graph workflow
	WorkflowTypeDAG WorkflowType = "dag"
)

// WorkflowMode represents the workflow execution mode
type WorkflowMode string

const (
	// WorkflowModeStream represents a streaming workflow
	WorkflowModeStream WorkflowMode = "stream"
	// WorkflowModeBatch represents a batch workflow
	WorkflowModeBatch WorkflowMode = "batch"
)

// WorkflowState represents the workflow state
type WorkflowState string

const (
	// WorkflowStateCreated represents a created workflow
	WorkflowStateCreated WorkflowState = "created"
	// WorkflowStateRunning represents a running workflow
	WorkflowStateRunning WorkflowState = "running"
	// WorkflowStatePaused represents a paused workflow
	WorkflowStatePaused WorkflowState = "paused"
	// WorkflowStateCompleted represents a completed workflow
	WorkflowStateCompleted WorkflowState = "completed"
	// WorkflowStateFailed represents a failed workflow
	WorkflowStateFailed WorkflowState = "failed"
	// WorkflowStateCancelled represents a cancelled workflow
	WorkflowStateCancelled WorkflowState = "cancelled"
)

// StepType represents the step type
type StepType string

const (
	// StepTypeTask represents a task step
	StepTypeTask StepType = "task"
	// StepTypeSubWorkflow represents a sub-workflow step
	StepTypeSubWorkflow StepType = "sub-workflow"
	// StepTypeParallel represents a parallel step
	StepTypeParallel StepType = "parallel"
	// StepTypeCondition represents a condition step
	StepTypeCondition StepType = "condition"
	// StepTypeSwitch represents a switch step
	StepTypeSwitch StepType = "switch"
	// StepTypeLoop represents a loop step
	StepTypeLoop StepType = "loop"
	// StepTypeWait represents a wait step
	StepTypeWait StepType = "wait"
)

// StepState represents the step state
type StepState string

const (
	// StepStatePending represents a pending step
	StepStatePending StepState = "pending"
	// StepStateRunning represents a running step
	StepStateRunning StepState = "running"
	// StepStateCompleted represents a completed step
	StepStateCompleted StepState = "completed"
	// StepStateFailed represents a failed step
	StepStateFailed StepState = "failed"
	// StepStateSkipped represents a skipped step
	StepStateSkipped StepState = "skipped"
)

// RetryPolicy represents the retry policy
type RetryPolicy struct {
	// MaxRetries is the maximum number of retries
	MaxRetries int `json:"maxRetries"`
	// Interval is the retry interval
	Interval time.Duration `json:"interval"`
	// BackoffFactor is the backoff factor
	BackoffFactor float64 `json:"backoffFactor"`
	// MaxInterval is the maximum retry interval
	MaxInterval time.Duration `json:"maxInterval"`
}

// ErrorHandlingPolicy represents the error handling policy
type ErrorHandlingPolicy struct {
	// Action is the error handling action
	Action string `json:"action"`
	// RetryPolicy is the retry policy
	RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"`
}

// Metadata represents the workflow metadata
type Metadata struct {
	// ID is the workflow ID
	ID string `json:"id"`
	// Name is the workflow name
	Name string `json:"name"`
	// Version is the workflow version
	Version string `json:"version"`
	// Description is the workflow description
	Description string `json:"description"`
	// Tags are the workflow tags
	Tags []string `json:"tags,omitempty"`
	// CreatedAt is the creation time
	CreatedAt time.Time `json:"createdAt"`
	// UpdatedAt is the update time
	UpdatedAt time.Time `json:"updatedAt"`
}

// WorkflowConfig represents the workflow configuration
type WorkflowConfig struct {
	// Type is the workflow type
	Type WorkflowType `json:"type"`
	// Mode is the workflow mode
	Mode WorkflowMode `json:"mode"`
	// Timeout is the workflow timeout
	Timeout time.Duration `json:"timeout,omitempty"`
	// ErrorHandling is the error handling policy
	ErrorHandling *ErrorHandlingPolicy `json:"errorHandling,omitempty"`
	// StateStore is the state store configuration
	StateStore map[string]interface{} `json:"stateStore,omitempty"`
}

// StepConfig represents the step configuration
type StepConfig struct {
	// Type is the step type
	Type StepType `json:"type"`
	// Timeout is the step timeout
	Timeout time.Duration `json:"timeout,omitempty"`
	// RetryPolicy is the retry policy
	RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"`
	// ErrorHandling is the error handling policy
	ErrorHandling *ErrorHandlingPolicy `json:"errorHandling,omitempty"`
}

// TaskConfig represents the task configuration
type TaskConfig struct {
	// Function is the task function
	Function string `json:"function"`
	// Inputs are the task inputs
	Inputs map[string]interface{} `json:"inputs,omitempty"`
	// Outputs are the task outputs
	Outputs map[string]string `json:"outputs,omitempty"`
}

// ConditionConfig represents the condition configuration
type ConditionConfig struct {
	// Condition is the condition expression
	Condition string `json:"condition"`
	// TrueStep is the step to execute if the condition is true
	TrueStep string `json:"trueStep"`
	// FalseStep is the step to execute if the condition is false
	FalseStep string `json:"falseStep,omitempty"`
}

// SwitchCase represents a switch case
type SwitchCase struct {
	// Value is the case value
	Value interface{} `json:"value"`
	// Step is the step to execute
	Step string `json:"step"`
}

// SwitchConfig represents the switch configuration
type SwitchConfig struct {
	// Expression is the switch expression
	Expression string `json:"expression"`
	// Cases are the switch cases
	Cases []*SwitchCase `json:"cases"`
	// Default is the default step
	Default string `json:"default,omitempty"`
}

// LoopConfig represents the loop configuration
type LoopConfig struct {
	// Mode is the loop mode
	Mode string `json:"mode"`
	// Count is the loop count
	Count int `json:"count,omitempty"`
	// Condition is the loop condition
	Condition string `json:"condition,omitempty"`
	// Collection is the collection to iterate
	Collection string `json:"collection,omitempty"`
	// Step is the step to execute
	Step string `json:"step"`
}

// WaitConfig represents the wait configuration
type WaitConfig struct {
	// Duration is the wait duration
	Duration time.Duration `json:"duration,omitempty"`
	// Until is the wait condition
	Until string `json:"until,omitempty"`
}

// Step represents a workflow step
type Step struct {
	// ID is the step ID
	ID string `json:"id"`
	// Name is the step name
	Name string `json:"name"`
	// Description is the step description
	Description string `json:"description,omitempty"`
	// Config is the step configuration
	Config *StepConfig `json:"config"`
	// Task is the task configuration
	Task *TaskConfig `json:"task,omitempty"`
	// Condition is the condition configuration
	Condition *ConditionConfig `json:"condition,omitempty"`
	// Switch is the switch configuration
	Switch *SwitchConfig `json:"switch,omitempty"`
	// Loop is the loop configuration
	Loop *LoopConfig `json:"loop,omitempty"`
	// Wait is the wait configuration
	Wait *WaitConfig `json:"wait,omitempty"`
	// Next is the next step ID
	Next string `json:"next,omitempty"`
	// Dependencies are the step dependencies
	Dependencies []string `json:"dependencies,omitempty"`
}

// Workflow represents a workflow definition
type Workflow struct {
	// Metadata is the workflow metadata
	Metadata *Metadata `json:"metadata"`
	// Config is the workflow configuration
	Config *WorkflowConfig `json:"config"`
	// Inputs are the workflow inputs
	Inputs map[string]interface{} `json:"inputs,omitempty"`
	// Outputs are the workflow outputs
	Outputs map[string]string `json:"outputs,omitempty"`
	// Steps are the workflow steps
	Steps map[string]*Step `json:"steps"`
	// Start is the start step ID
	Start string `json:"start"`
}

// Validate validates the workflow definition
func (w *Workflow) Validate() error {
	// Check metadata
	if w.Metadata == nil {
		return fmt.Errorf("workflow metadata is required")
	}
	if w.Metadata.ID == "" {
		return fmt.Errorf("workflow ID is required")
	}
	if w.Metadata.Name == "" {
		return fmt.Errorf("workflow name is required")
	}
	if w.Metadata.Version == "" {
		return fmt.Errorf("workflow version is required")
	}

	// Check configuration
	if w.Config == nil {
		return fmt.Errorf("workflow configuration is required")
	}

	// Check steps
	if len(w.Steps) == 0 {
		return fmt.Errorf("workflow must have at least one step")
	}

	// Check start step
	if w.Start == "" {
		return fmt.Errorf("workflow start step is required")
	}
	if _, ok := w.Steps[w.Start]; !ok {
		return fmt.Errorf("workflow start step %s not found", w.Start)
	}

	// Validate steps
	for id, step := range w.Steps {
		if step.ID != id {
			return fmt.Errorf("step ID mismatch: %s != %s", step.ID, id)
		}
		if step.Name == "" {
			return fmt.Errorf("step name is required for step %s", id)
		}
		if step.Config == nil {
			return fmt.Errorf("step configuration is required for step %s", id)
		}

		// Validate step configuration
		switch step.Config.Type {
		case StepTypeTask:
			if step.Task == nil {
				return fmt.Errorf("task configuration is required for task step %s", id)
			}
			if step.Task.Function == "" {
				return fmt.Errorf("task function is required for task step %s", id)
			}
		case StepTypeCondition:
			if step.Condition == nil {
				return fmt.Errorf("condition configuration is required for condition step %s", id)
			}
			if step.Condition.Condition == "" {
				return fmt.Errorf("condition expression is required for condition step %s", id)
			}
			if step.Condition.TrueStep == "" {
				return fmt.Errorf("true step is required for condition step %s", id)
			}
			if _, ok := w.Steps[step.Condition.TrueStep]; !ok {
				return fmt.Errorf("true step %s not found for condition step %s", step.Condition.TrueStep, id)
			}
			if step.Condition.FalseStep != "" {
				if _, ok := w.Steps[step.Condition.FalseStep]; !ok {
					return fmt.Errorf("false step %s not found for condition step %s", step.Condition.FalseStep, id)
				}
			}
		case StepTypeSwitch:
			if step.Switch == nil {
				return fmt.Errorf("switch configuration is required for switch step %s", id)
			}
			if step.Switch.Expression == "" {
				return fmt.Errorf("switch expression is required for switch step %s", id)
			}
			if len(step.Switch.Cases) == 0 {
				return fmt.Errorf("switch cases are required for switch step %s", id)
			}
			for i, c := range step.Switch.Cases {
				if c.Step == "" {
					return fmt.Errorf("case step is required for case %d in switch step %s", i, id)
				}
				if _, ok := w.Steps[c.Step]; !ok {
					return fmt.Errorf("case step %s not found for case %d in switch step %s", c.Step, i, id)
				}
			}
			if step.Switch.Default != "" {
				if _, ok := w.Steps[step.Switch.Default]; !ok {
					return fmt.Errorf("default step %s not found for switch step %s", step.Switch.Default, id)
				}
			}
		case StepTypeLoop:
			if step.Loop == nil {
				return fmt.Errorf("loop configuration is required for loop step %s", id)
			}
			if step.Loop.Mode == "" {
				return fmt.Errorf("loop mode is required for loop step %s", id)
			}
			if step.Loop.Step == "" {
				return fmt.Errorf("loop step is required for loop step %s", id)
			}
			if _, ok := w.Steps[step.Loop.Step]; !ok {
				return fmt.Errorf("loop step %s not found for loop step %s", step.Loop.Step, id)
			}
		case StepTypeWait:
			if step.Wait == nil {
				return fmt.Errorf("wait configuration is required for wait step %s", id)
			}
			if step.Wait.Duration == 0 && step.Wait.Until == "" {
				return fmt.Errorf("wait duration or condition is required for wait step %s", id)
			}
		}

		// Check next step
		if step.Next != "" {
			if _, ok := w.Steps[step.Next]; !ok {
				return fmt.Errorf("next step %s not found for step %s", step.Next, id)
			}
		}

		// Check dependencies
		for _, dep := range step.Dependencies {
			if _, ok := w.Steps[dep]; !ok {
				return fmt.Errorf("dependency step %s not found for step %s", dep, id)
			}
		}
	}

	return nil
}

// ToJSON converts the workflow to JSON
func (w *Workflow) ToJSON() ([]byte, error) {
	return json.Marshal(w)
}

// FromJSON parses the workflow from JSON
func FromJSON(data []byte) (*Workflow, error) {
	var workflow Workflow
	err := json.Unmarshal(data, &workflow)
	if err != nil {
		return nil, err
	}
	return &workflow, nil
}

// NewWorkflow creates a new workflow
func NewWorkflow(id, name, version string, workflowType WorkflowType, workflowMode WorkflowMode) *Workflow {
	now := time.Now()
	return &Workflow{
		Metadata: &Metadata{
			ID:        id,
			Name:      name,
			Version:   version,
			CreatedAt: now,
			UpdatedAt: now,
		},
		Config: &WorkflowConfig{
			Type: workflowType,
			Mode: workflowMode,
		},
		Inputs:  make(map[string]interface{}),
		Outputs: make(map[string]string),
		Steps:   make(map[string]*Step),
	}
}

// AddStep adds a step to the workflow
func (w *Workflow) AddStep(step *Step) error {
	if step.ID == "" {
		return fmt.Errorf("step ID is required")
	}
	if _, ok := w.Steps[step.ID]; ok {
		return fmt.Errorf("step %s already exists", step.ID)
	}
	w.Steps[step.ID] = step
	return nil
}

// SetStartStep sets the start step
func (w *Workflow) SetStartStep(stepID string) error {
	if _, ok := w.Steps[stepID]; !ok {
		return fmt.Errorf("step %s not found", stepID)
	}
	w.Start = stepID
	return nil
}

// GetStep gets a step by ID
func (w *Workflow) GetStep(stepID string) (*Step, error) {
	step, ok := w.Steps[stepID]
	if !ok {
		return nil, fmt.Errorf("step %s not found", stepID)
	}
	return step, nil
}

// RemoveStep removes a step from the workflow
func (w *Workflow) RemoveStep(stepID string) error {
	if _, ok := w.Steps[stepID]; !ok {
		return fmt.Errorf("step %s not found", stepID)
	}

	// Check if the step is the start step
	if w.Start == stepID {
		return fmt.Errorf("cannot remove start step %s", stepID)
	}

	// Check if the step is referenced by other steps
	for id, step := range w.Steps {
		if step.Next == stepID {
			return fmt.Errorf("step %s is referenced as next step by step %s", stepID, id)
		}
		for _, dep := range step.Dependencies {
			if dep == stepID {
				return fmt.Errorf("step %s is referenced as dependency by step %s", stepID, id)
			}
		}
		if step.Config.Type == StepTypeCondition && step.Condition != nil {
			if step.Condition.TrueStep == stepID {
				return fmt.Errorf("step %s is referenced as true step by condition step %s", stepID, id)
			}
			if step.Condition.FalseStep == stepID {
				return fmt.Errorf("step %s is referenced as false step by condition step %s", stepID, id)
			}
		}
		if step.Config.Type == StepTypeSwitch && step.Switch != nil {
			for i, c := range step.Switch.Cases {
				if c.Step == stepID {
					return fmt.Errorf("step %s is referenced as case step by switch step %s (case %d)", stepID, id, i)
				}
			}
			if step.Switch.Default == stepID {
				return fmt.Errorf("step %s is referenced as default step by switch step %s", stepID, id)
			}
		}
		if step.Config.Type == StepTypeLoop && step.Loop != nil {
			if step.Loop.Step == stepID {
				return fmt.Errorf("step %s is referenced as loop step by loop step %s", stepID, id)
			}
		}
	}

	// Remove the step
	delete(w.Steps, stepID)
	return nil
}

// NewTaskStep creates a new task step
func NewTaskStep(id, name, function string) *Step {
	return &Step{
		ID:   id,
		Name: name,
		Config: &StepConfig{
			Type: StepTypeTask,
		},
		Task: &TaskConfig{
			Function: function,
			Inputs:   make(map[string]interface{}),
			Outputs:  make(map[string]string),
		},
	}
}

// NewConditionStep creates a new condition step
func NewConditionStep(id, name, condition, trueStep, falseStep string) *Step {
	return &Step{
		ID:   id,
		Name: name,
		Config: &StepConfig{
			Type: StepTypeCondition,
		},
		Condition: &ConditionConfig{
			Condition: condition,
			TrueStep:  trueStep,
			FalseStep: falseStep,
		},
	}
}

// NewSwitchStep creates a new switch step
func NewSwitchStep(id, name, expression string) *Step {
	return &Step{
		ID:   id,
		Name: name,
		Config: &StepConfig{
			Type: StepTypeSwitch,
		},
		Switch: &SwitchConfig{
			Expression: expression,
			Cases:      make([]*SwitchCase, 0),
		},
	}
}

// AddSwitchCase adds a case to a switch step
func (s *Step) AddSwitchCase(value interface{}, step string) error {
	if s.Config.Type != StepTypeSwitch {
		return fmt.Errorf("step %s is not a switch step", s.ID)
	}
	if s.Switch == nil {
		return fmt.Errorf("switch configuration is missing for step %s", s.ID)
	}
	s.Switch.Cases = append(s.Switch.Cases, &SwitchCase{
		Value: value,
		Step:  step,
	})
	return nil
}

// NewLoopStep creates a new loop step
func NewLoopStep(id, name, mode, step string) *Step {
	return &Step{
		ID:   id,
		Name: name,
		Config: &StepConfig{
			Type: StepTypeLoop,
		},
		Loop: &LoopConfig{
			Mode: mode,
			Step: step,
		},
	}
}

// NewWaitStep creates a new wait step
func NewWaitStep(id, name string, duration time.Duration) *Step {
	return &Step{
		ID:   id,
		Name: name,
		Config: &StepConfig{
			Type: StepTypeWait,
		},
		Wait: &WaitConfig{
			Duration: duration,
		},
	}
}

// SetNextStep sets the next step
func (s *Step) SetNextStep(nextStep string) {
	s.Next = nextStep
}

// AddDependency adds a dependency to the step
func (s *Step) AddDependency(dependency string) {
	if s.Dependencies == nil {
		s.Dependencies = make([]string, 0)
	}
	s.Dependencies = append(s.Dependencies, dependency)
}

// SetInput sets an input value for a task step
func (s *Step) SetInput(name string, value interface{}) error {
	if s.Config.Type != StepTypeTask {
		return fmt.Errorf("step %s is not a task step", s.ID)
	}
	if s.Task == nil {
		return fmt.Errorf("task configuration is missing for step %s", s.ID)
	}
	s.Task.Inputs[name] = value
	return nil
}

// SetOutput sets an output mapping for a task step
func (s *Step) SetOutput(name, expression string) error {
	if s.Config.Type != StepTypeTask {
		return fmt.Errorf("step %s is not a task step", s.ID)
	}
	if s.Task == nil {
		return fmt.Errorf("task configuration is missing for step %s", s.ID)
	}
	s.Task.Outputs[name] = expression
	return nil
}

// SetTimeout sets the timeout for a step
func (s *Step) SetTimeout(timeout time.Duration) {
	if s.Config == nil {
		s.Config = &StepConfig{}
	}
	s.Config.Timeout = timeout
}

// SetRetryPolicy sets the retry policy for a step
func (s *Step) SetRetryPolicy(maxRetries int, interval time.Duration, backoffFactor float64, maxInterval time.Duration) {
	if s.Config == nil {
		s.Config = &StepConfig{}
	}
	s.Config.RetryPolicy = &RetryPolicy{
		MaxRetries:    maxRetries,
		Interval:      interval,
		BackoffFactor: backoffFactor,
		MaxInterval:   maxInterval,
	}
}

// SetErrorHandlingPolicy sets the error handling policy for a step
func (s *Step) SetErrorHandlingPolicy(action string) {
	if s.Config == nil {
		s.Config = &StepConfig{}
	}
	s.Config.ErrorHandling = &ErrorHandlingPolicy{
		Action: action,
	}
}

// SetErrorHandlingPolicyWithRetry sets the error handling policy with retry for a step
func (s *Step) SetErrorHandlingPolicyWithRetry(action string, maxRetries int, interval time.Duration, backoffFactor float64, maxInterval time.Duration) {
	if s.Config == nil {
		s.Config = &StepConfig{}
	}
	s.Config.ErrorHandling = &ErrorHandlingPolicy{
		Action: action,
		RetryPolicy: &RetryPolicy{
			MaxRetries:    maxRetries,
			Interval:      interval,
			BackoffFactor: backoffFactor,
			MaxInterval:   maxInterval,
		},
	}
}
